import concurrent
from concurrent import futures

import toml

from service.ctrip_upload_policy import *
from service.qunar_upload_policy import *
from service.feizhu_upload_policy import *
from service.meituan_upload_policy import *


# -*- coding: utf-8 -*-
# @Author : HJH
# @Time : 2024/1/23 14:54
# @Use :

def log_config():
    # 添加输出处理程序，将日志输出到文件
    logger.add("./logs/info_file.log", level="INFO")
    logger.add("./logs/error_file.log", level="ERROR")


def send_api_request(path: str):
    """
    发送请求的函数，这里使用requests库发送请求。
    :return: pandas df对象
    """
    url = f"http://222.89.84.32:9093/static/price/{path}"

    headers = {"content-type": "application/json"}

    logger.info("数据请求中... ...")

    # logger.info(response.text)
    # 检查响应状态码是否为200
    # if response.status_code == 200:
    #     rs_json = json.loads(response.text)
    #     return rs_json
    #
    # else:
    #     logger.error('数据请求失败：{}'.format(response.text))
    #     raise RuntimeError("发生错误，程序停止")
    """
    请求接口
    """
    max_tries = 5  # 最大尝试次数
    num_tries = 0  # 已尝试次数
    sleep_time2 = 3  # 设置等待时间
    while num_tries < max_tries:
        try:
            response = requests.request("GET", url, headers=headers, timeout=10000)
            if response.status_code != 200:
                num_tries += 1
                if num_tries >= max_tries:
                    # 如果已经尝试到最大次数，跳出循环，放弃重试
                    logger.error(f"重试{max_tries}次后仍然失败，放弃重试。")
                    return None
                continue
            # 成功请求到数据，直接返回
            return json.loads(response.text)
        except requests.exceptions.RequestException as e:
            logger.error(e)
            num_tries += 1
            if num_tries >= max_tries:
                # 如果已经尝试到最大次数，跳出循环，放弃重试
                logger.error(f"重试{max_tries}次后仍然失败，放弃重试。")
                return None
            # 打印日志，并且睡眠
            logger.info(f"请求失败，休眠 {sleep_time2} 秒后进行第 {num_tries + 1} 次尝试。")
            time.sleep(sleep_time2)


def send_py_api_request(path: str):
    """
    发送请求的函数，这里使用requests库发送请求。
    :return: pandas df对象
    """
    url = f"http://192.168.110.45:6183/auto_upload/{path}"

    headers = {"content-type": "application/json"}

    logger.info("数据请求中... ...")

    # logger.info(response.text)
    # 检查响应状态码是否为200
    # if response.status_code == 200:
    #     rs_json = json.loads(response.text)
    #     return rs_json
    #
    # else:
    #     logger.error('数据请求失败：{}'.format(response.text))
    #     raise RuntimeError("发生错误，程序停止")
    """
    请求接口
    """
    max_tries = 5  # 最大尝试次数
    num_tries = 0  # 已尝试次数
    sleep_time2 = 3  # 设置等待时间
    while num_tries < max_tries:
        try:
            response = requests.request("GET", url, headers=headers, timeout=10000)
            if response.status_code != 200:
                num_tries += 1
                if num_tries >= max_tries:
                    # 如果已经尝试到最大次数，跳出循环，放弃重试
                    logger.error(f"重试{max_tries}次后仍然失败，放弃重试。")
                    return None
                continue
            # 成功请求到数据，直接返回
            return json.loads(response.text)
        except requests.exceptions.RequestException as e:
            logger.error(e)
            num_tries += 1
            if num_tries >= max_tries:
                # 如果已经尝试到最大次数，跳出循环，放弃重试
                logger.error(f"重试{max_tries}次后仍然失败，放弃重试。")
                return None
            # 打印日志，并且睡眠
            logger.info(f"请求失败，休眠 {sleep_time2} 秒后进行第 {num_tries + 1} 次尝试。")
            time.sleep(sleep_time2)


def get_key(item):
    """
    去重字段
    :param item:
    :return:
    """
    return (
        item.get('depDate'), item.get('flightNo'), item.get('carrier'), item.get('dep'), item.get('arr'),
        item.get('cabin'),
        item.get('costPrice'),
        item.get('price'))


def check_param(ck_param):
    """
    检查配置参数是否符合要求
    :param ck_param: 配置参数
    :return: 是否符合
    """
    if ck_param is None:
        return False
    if not isinstance(ck_param, dict):
        return False
    if ck_param.get('policyCode') is None:
        return False
    if ck_param.get('policyCode') == '':
        return False
    return True


def get_xc_data():
    """
    获取携程数据
    :return: 去重后数据
    """
    # 发送请求
    xc_rs_json = send_api_request('getXcData')
    if xc_rs_json is None:
        return None

    xc_rs_data = xc_rs_json.get('data')  # 获取列表数据
    logger.info(f"【获取列表数据长度】: {len(xc_rs_data)}")
    unique_items = set(get_key(item) for item in xc_rs_data)  # 根据字段去重
    result = [dict(zip(['depDate', 'flightNo', 'carrier', 'dep', 'arr', 'cabin', 'costPrice', 'price'], key)) for key in
              unique_items]

    # 过滤完数据长度
    logger.info(f"【去重数据长度】: {len(result)}")
    return result


def get_mu_data(source):
    """
    获取东航数据
    :return: 数据
    """
    # 发送请求
    mu_rs_json = send_api_request(f'getMuData/{source}')
    if mu_rs_json is None:
        return None

    mu_rs_data = mu_rs_json.get('data')  # 获取列表数据
    logger.info(f"【获取列表数据长度】: {len(mu_rs_data)}")

    return mu_rs_data


def get_tc_data():
    """
    获取同程数据
    :return: 数据
    """
    # 发送请求
    mu_rs_json = send_py_api_request('getTcData')
    if mu_rs_json is None:
        return None

    tc_rs_list = mu_rs_json.get('data')  # 获取列表数据
    logger.info(f"【获取列表数据长度】: {len(tc_rs_list)}")

    return tc_rs_list


def del_policy(dp_param, del_list):
    """
    根据指令删除政策
    :param del_list: 需要删除的政策
    :param dp_param: 平台参数
    :return:
    """
    if not isinstance(dp_param, dict):
        RuntimeError("配置参数有误，请检查后重新启动")

    pc = dp_param.get('policyCode')
    pf = dp_param.get('platform')
    if pf == 'QN' or pf == 'QN-GX':
        del_qn_req = del_qn_policy(pc, del_list)
        logger.info("【去哪儿exw单程统一删除政策】: 请求中... ...")
        send_qn_request(del_qn_req, 'exw')
        logger.info(f"【去哪儿exw单程统一政策删除成功】: {pc}")
    elif pf == 'FZ' or pf == 'FZ-GX':
        del_fz_req = del_fz_policy(pc, dp_param.get('agent_id', 5252), dp_param.get('del_fuzzy_query', False), del_list)
        logger.info("【飞猪特殊政策删除】: 请求中... ...")
        if del_list is not None and len(del_list) == 0:
            pass
        else:
            send_fz_request(del_fz_req, dp_param.get('agent_id', 5252))
        logger.info(f"【飞猪特殊政策删除成功】: {pc}")
    elif pf == 'FX':
        del_qn_req = del_qn_policy(pc, del_list)
        logger.info("【去哪儿fio单程统一删除政策】: 请求中... ...")
        send_qn_request(del_qn_req, 'fio')
        logger.info(f"【去哪儿fio单程统一政策删除成功】: {pc}")
    elif pf == 'MT':
        del_mt_req = del_mt_policy(pc, del_list)
        logger.info("【美团单程预付删除政策】: 请求中... ...")
        send_mt_request(del_mt_req)
        logger.info(f"【美团单程预付政策删除成功】: {pc}")
    elif pf == 'XC':
        del_xc_req = del_xc_policy(pc, dp_param.get('isFuzzyMatch'))
        logger.info("【携程特殊政策删除政策】: 请求中... ...")
        send_xc_request(del_xc_req, 'SpecialPolicyDelete')
        logger.info(f"【携程特殊政策删除成功】: {pc}")


def task_function(tf_res_data: list, tf_param: dict):
    """
    根据平台标识区分方法调用
    :param tf_res_data: 政策数据
    :param tf_param: 配置参数
    """
    tf_pf = tf_param.get('platform')
    if tf_pf == 'QN':
        run_qn_upload(tf_res_data, tf_param, 'exw')
    elif tf_pf == 'QN-GX':
        run_qn_gx_upload(tf_res_data, tf_param, 'exw')
    elif tf_pf == 'FZ':
        run_fz_upload(tf_res_data, tf_param)
    elif tf_pf == 'FZ-GX':
        run_fz_gx_upload(tf_res_data, tf_param)
    elif tf_pf == 'FX':
        run_qn_upload(tf_res_data, tf_param, 'fio')
    elif tf_pf == 'MT':
        run_mt_upload(tf_res_data, tf_param)
    elif tf_pf == 'XC':
        run_xc_upload(tf_res_data, tf_param)


def to_upload(tu_res_data, tu_policy):
    """
    使用多线程执行任务
    :param tu_res_data: 政策数据
    :param tu_policy: 配置参数
    """
    # 创建线程池
    with (concurrent.futures.ThreadPoolExecutor(max_workers=len(tu_policy.values())) as executor):
        # 存储任务的 future 对象
        future_list = []

        # 向线程池提交任务
        for tu_value in tu_policy.values():
            if check_param(tu_value):

                # 每次上传删除之前政策
                if is_del_always == 1 and data_source != 'DH-SG':
                    del_policy(tu_value, None)

                # 东航闪购特殊处理
                if data_source == 'DH-SG' or data_source == 'DH-ZX':
                    # 需要上传的政策数据
                    need_up_data = [up_data for up_data in tu_res_data if
                                    up_data.get('isUp') == 0 and up_data.get('status') == 0]
                    # 需要删除的政策数据
                    need_del_list = [up_data for up_data in tu_res_data if
                                     up_data.get('isUp') == 5 and up_data.get('status') == 1]
                    del_policy(tu_value, need_del_list)
                    future = executor.submit(task_function, need_up_data, tu_value)
                else:
                    future = executor.submit(task_function, tu_res_data, tu_value)

                # task_function(tu_xc_res, tu_value)
                future_list.append(future)


def load_config():
    """
    加载配置文件，并校验是否合法
    :return: 字典格式参数
    """
    config_file = input("请输入配置文件名称: ")
    while not (os.path.exists(f'./config/{config_file}.toml') and os.path.isfile(f'./config/{config_file}.toml')):
        print(f"【配置文件读取错误】: 未找到指定文件{config_file}.toml\n\n")
        config_file = input("请输入配置文件名称: ")

    with open(f'./config/{config_file}.toml', 'r', encoding="utf-8") as f:
        cfg = toml.load(f)
        channel_list = [key for key in cfg.keys() if key.isupper()]
        print(f"已配置渠道: {','.join(channel_list)}")
        upload_str = input("请选择需要上传的渠道(默认全部): ")
        if upload_str is not None and upload_str != '':
            upload_strs = upload_str.split(',')
            for cn in channel_list:
                if cn not in upload_strs:
                    cfg.pop(cn)

        logger.info(f"【读取到配置文件参数】: {cfg}")
        return cfg


if __name__ == '__main__':
    # 初始化日志配置
    log_config()

    # ************************* 读取配置文件 *************************
    config = load_config()

    data_source = config.get('data_source')
    is_del_always = config.get('is_del_always')
    sleep_time = config.get('sleep_time')

    # ************************* 清除已上传数据标识 *************************
    if config.get('is_up') == 1:
        if data_source == "CZ-TC":
            rs_us_data = send_py_api_request(f'updateStatus')
        else:
            rs_us_data = send_api_request(f'updateStatus/{data_source}')
        logger.info(f"【清除已上传数据标识成功】: {rs_us_data.get('data')}")

    # ************************* 删除平台政策 *************************
    if config.get('is_del') == 1:
        for value in config.values():
            if check_param(value):
                del_policy(value, None)

    # ************************* 循环上传政策 *************************
    while True:
        # 获取数据源数据
        res_ds_data = []
        if data_source == 'XC-ZC':
            res_ds_data = get_xc_data()
        elif data_source == 'DH-ZX':
            res_ds_data = get_mu_data(data_source)
        elif data_source == 'DH-SG':
            res_ds_data = get_mu_data(data_source)
        elif data_source == 'CZ-TC':
            res_ds_data = get_tc_data()

        # 上传政策
        if res_ds_data is not None and len(res_ds_data) > 0:
            to_upload(res_ds_data, config)

        logger.info(f"下次执行时间 {int(sleep_time) * 60}秒 后... ...")
        time.sleep(int(sleep_time) * 60)
